use windows::{
    Win32::{
        Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM},
        Graphics::Gdi::UpdateWindow,
        System::LibraryLoader::GetModuleHandleW,
        UI::WindowsAndMessaging::{
            CREATESTRUCTW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DispatchMessageW,
            GWLP_USERDATA, GetMessageW, GetWindowLongPtrW, GetWindowTextW, HMENU, IDC_ARROW,
            LB_ADDSTRING, LB_ERR, LB_GETCURSEL, LB_RESETCONTENT, LoadCursorW, MSG, PostQuitMessage, RegisterClassW, SW_SHOWDEFAULT,
            SendMessageW, SetProcessDPIAware, SetWindowLongPtrW, SetWindowTextW, ShowWindow,
            TranslateMessage, WINDOW_EX_STYLE, WM_COMMAND, WM_CREATE, WM_DESTROY, WNDCLASSW,
            WS_BORDER, WS_CHILD, WS_OVERLAPPEDWINDOW, WS_VISIBLE, WS_VSCROLL,
        },
    },
    core::w,
};

const IDC_EDIT: usize = 1001;
const IDC_ADDBUTTON: usize = 1002;
const IDC_SEARCH: usize = 1003;
const IDC_REMOVEBUTTON: usize = 1004;
const IDC_LISTBOX: usize = 1005;
#[derive(Default)]
struct AppState {
    h_input: HWND,
    h_add_button: HWND,
    h_search: HWND,
    h_remove_button: HWND,
    h_list_box: HWND,
    todo_list: Vec<[u16; 256]>,
}

fn main() {
    unsafe {
        let hinstance = GetModuleHandleW(None).unwrap();
        win_main(hinstance.into());
    }
}
fn win_main(hinstance: HINSTANCE) {
    unsafe {
        SetProcessDPIAware().unwrap();
        let state = Box::new(AppState::default());
        let raw_state = Box::into_raw(state);
        let class_name = w!("ToDoApp");
        let wc = WNDCLASSW {
            lpfnWndProc: Some(wnd_proc),
            hInstance: hinstance,
            //cbWndExtra: size_of::<GlobalData>() as _,
            lpszClassName: class_name,
            hCursor: LoadCursorW(None, IDC_ARROW).unwrap(),
            ..Default::default()
        };
        RegisterClassW(&wc);
        let hwnd = CreateWindowExW(
            WINDOW_EX_STYLE(0),
            class_name,
            w!("TODO"),
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            400,
            300,
            None,
            None,
            Some(hinstance),
            Some(raw_state as _),
        )
        .unwrap();

        let _ = ShowWindow(hwnd, SW_SHOWDEFAULT);
        let _ = UpdateWindow(hwnd);
        let mut msg = MSG::default();
        while GetMessageW(&mut msg, None, 0, 0).into() {
            let _ = TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }
}
fn get_app_state(hwnd: HWND) -> &'static mut AppState {
    unsafe {
        let state = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut AppState;
        &mut *state
    }
}
fn refresh_list(state: &mut AppState) {
    unsafe {
        SendMessageW(state.h_list_box, LB_RESETCONTENT, None, None);
        for task in &state.todo_list {
            SendMessageW(
                state.h_list_box,
                LB_ADDSTRING,
                None,
                Some(LPARAM(task.as_ptr() as _)),
            );
        }
    }
}
unsafe extern "system" fn wnd_proc(
    hwnd: HWND,
    msg: u32,
    wparam: WPARAM,
    lparam: LPARAM,
) -> LRESULT {
    unsafe {
        match msg {
            WM_CREATE => {
                let p_create: *mut CREATESTRUCTW = lparam.0 as _;
                let state = (*p_create).lpCreateParams as *mut AppState;
                SetWindowLongPtrW(hwnd, GWLP_USERDATA, state as _);
                let state = &mut *state;
                println!("Create window");
                state.h_input = CreateWindowExW(
                    WINDOW_EX_STYLE(0),
                    w!("EDIT"),
                    None,
                    WS_CHILD | WS_VISIBLE | WS_BORDER,
                    0,
                    0,
                    300,
                    30,
                    Some(hwnd),
                    Some(HMENU(IDC_EDIT as _)),
                    None,
                    None,
                )
                .unwrap();

                state.h_add_button = CreateWindowExW(
                    WINDOW_EX_STYLE(0),
                    w!("BUTTON"),
                    w!("Add"),
                    WS_CHILD | WS_VISIBLE,
                    300,
                    0,
                    80,
                    30,
                    Some(hwnd),
                    Some(HMENU(IDC_ADDBUTTON as _)),
                    None,
                    None,
                )
                .unwrap();
                state.h_search = CreateWindowExW(
                    WINDOW_EX_STYLE(0),
                    w!("EDIT"),
                    None,
                    WS_CHILD | WS_VISIBLE | WS_BORDER,
                    0,
                    30,
                    300,
                    30,
                    Some(hwnd),
                    Some(HMENU(IDC_SEARCH as _)),
                    None,
                    None,
                )
                .unwrap();

                state.h_remove_button = CreateWindowExW(
                    WINDOW_EX_STYLE(0),
                    w!("BUTTON"),
                    w!("Remove"),
                    WS_CHILD | WS_VISIBLE,
                    300,
                    30,
                    80,
                    30,
                    Some(hwnd),
                    Some(HMENU(IDC_REMOVEBUTTON as _)),
                    None,
                    None,
                )
                .unwrap();

                state.h_list_box = CreateWindowExW(
                    WINDOW_EX_STYLE(0),
                    w!("LISTBOX"),
                    None,
                    WS_CHILD | WS_VISIBLE | WS_VSCROLL,
                    0,
                    60,
                    400,
                    300 - 60,
                    Some(hwnd),
                    Some(HMENU(IDC_LISTBOX as _)),
                    None,
                    None,
                )
                .unwrap();
            }
            WM_COMMAND => {
                let state = get_app_state(hwnd);
                match wparam.0 {
                    IDC_ADDBUTTON => {
                        let mut buf = [0; 256];
                        if GetWindowTextW(state.h_input, &mut buf) > 0 {
                            state.todo_list.push(buf);
                            SetWindowTextW(state.h_input, w!("")).unwrap();
                            refresh_list(state);
                        }
                    }
                    IDC_REMOVEBUTTON => {
                        let select_index =
                            SendMessageW(state.h_list_box, LB_GETCURSEL, None, None).0;
                        if select_index != LB_ERR as _ {
                            state.todo_list.remove(select_index as _);
                            refresh_list(state);
                        }
                    }
                    _ => {}
                }
            }
            WM_DESTROY => {
                PostQuitMessage(0);
            }
            _ => return DefWindowProcW(hwnd, msg, wparam, lparam),
        }
    }
    LRESULT(0)
}
